﻿using System;

namespace ChessKit.Logic
{
	public partial class ProtoBoard
	{
		private const int BytesCount = 128;

		internal byte[] Squares { get; private set; }
		public Square WhiteKingPosition { get; private set; }
		public Square BlackKingPosition { get; private set; }

		/// <summary>Creates raw protoboard that has to be modified yet
		///   by deep copying source protoboard</summary>
		internal ProtoBoard(ProtoBoard source)
		{
			WhiteKingPosition = source.WhiteKingPosition;
			BlackKingPosition = source.BlackKingPosition;

			Squares = new byte[BytesCount];
			Buffer.BlockCopy(source.Squares, 0, Squares, 0, BytesCount);
		}

		internal ProtoBoard(Board source, Move move, MoveHints hints, PieceType promotion)
			: this(source.ProtoBoard)
		{
			var piece = (hints & MoveHints.Promotion) != 0
				? source.SideOnMove.ToPiece(promotion)
				: source[move.From];

			Squares[move.To] = (byte)piece;
			switch (piece)
			{
				case Piece.WhiteKing:
					WhiteKingPosition = move.To;
					break;
				case Piece.BlackKing:
					BlackKingPosition = move.To;
					break;
			}

			Squares[move.From] = 0;

			if ((hints & MoveHints.Special) != 0)
			{
				if ((hints & MoveHints.PawnDoubleMove) != 0)
				{
					EnPassantFile = move.From % 16;//file or rank?
				}
				else if ((hints & MoveHints.EnPassant) != 0)
				{
					Squares[move.To + (source.SideOnMove == PieceColor.White ? -16 : +16)] = 0;
				}
				else if (hints == (MoveHints.Castling | MoveHints.WhiteKingsideCastling)) // TODO: Move it up?
				{
					Squares[H1] = (byte)Piece.EmptyCell;
					Squares[F1] = (byte)Piece.WhiteRook;
				}
				else if (hints == (MoveHints.Castling | MoveHints.WhiteQueensideCastling))
				{
					Squares[A1] = (byte)Piece.EmptyCell;
					Squares[D1] = (byte)Piece.WhiteRook;
				}
				else if (hints == (MoveHints.Castling | MoveHints.BlackKingsideCastling))
				{
					Squares[H8] = (byte)Piece.EmptyCell;
					Squares[F8] = (byte)Piece.BlackRook;
				}
				else if (hints == (MoveHints.Castling | MoveHints.BlackQueensideCastling))
				{
					Squares[A8] = (byte)Piece.EmptyCell;
					Squares[D8] = (byte)Piece.BlackRook;
				}
			}

			CastlingAvailability = source.CastlingAvailability
			  & ~KilledAvailability(move.To)
			  & ~KilledAvailability(move.From);

			SideOnMove = source.SideOnMove.Invert();

			FiftyMoveClock = (hints & MoveHints.PawnCaptures) != 0 ? 0 : source.FiftyMoveClock + 1;

			MoveNumber = source.MoveNumber + (source.SideOnMove == PieceColor.Black ? 1 : 0);

		}
		private static CastlingAvailability KilledAvailability(int pos)
		{
			switch (pos)
			{
				case A1: return CastlingAvailability.WhiteQueen;
				case E1: return CastlingAvailability.White;
				case H1: return CastlingAvailability.WhiteKing;
				case A8: return CastlingAvailability.BlackQueen;
				case E8: return CastlingAvailability.Black;
				case H8: return CastlingAvailability.BlackKing;
				default: return CastlingAvailability.None;
			}
		}

		public bool CanBeValidMove(Square fromSquare, Square toSquare)
		{
			if (this[fromSquare] == 0) return false;
			if (!this[fromSquare].Is(SideOnMove)) return false;
			if (this[toSquare].Is(SideOnMove)) return false;
			return CanBeValidMove(this[fromSquare], fromSquare, toSquare);
		}

		internal ProtoBoard(BoardBuilder boardBuilder)
		{
			EnPassantFile = boardBuilder.EnPassantFile;
			MoveNumber = boardBuilder.MoveNumber;
			FiftyMoveClock = boardBuilder.FiftyMoveClock;
			SideOnMove = boardBuilder.SideOnMove;
			CastlingAvailability = boardBuilder.CastlingAvailability;
			WhiteKingPosition = boardBuilder.WhiteKingPosition.GetValueOrDefault();
			BlackKingPosition = boardBuilder.BlackKingPosition.GetValueOrDefault();
			Squares = new byte[BytesCount];
			Buffer.BlockCopy(boardBuilder.Squares, 0, Squares, 0, BytesCount);
		}

		public int? EnPassantFile { get; private set; }
		public int MoveNumber { get; private set; }
		public int FiftyMoveClock { get; private set; }
		public PieceColor SideOnMove { get; private set; }
		public CastlingAvailability CastlingAvailability { get; private set; }
		public MadeMove MoveBefore { get; private set; }

		public override string ToString()
		{
			return this.ToFenString();
		}
		public Piece this[Square index] { get { return (Piece)Squares[index]; } }

		public Board ToBoard() { return new Board(this); }
	}
}
